home *** CD-ROM | disk | FTP | other *** search
- #include <stdlib.h>
- #include <stdio.h>
- #include <malloc.h>
- #include <mem.h>
- #include <string.h>
- #include <dos.h>
- #include <time.h>
- #include <io.h>
- #include <fcntl.h>
- #include <sys\stat.h>
- #include <math.h>
- #include "loadpcx.h"
-
- typedef unsigned long ULONG;
- typedef unsigned short UINT;
- typedef unsigned char UCHAR;
-
- #define USE_ASSEMBLER 1 // Set to 0 to use C routines
-
- #define MAX_ANGLE 1280
- #define COLUMN_AMOUNT 2
- #define HALF_SCREEN (320/COLUMN_AMOUNT)
-
- #define M_PI 3.14159265358979323846
- #define RAYLENGTH 160
- #define MAXLENGTH 220
- #define WORLDX 320
- #define WORLDY 200
- #define WORLDSIZE 65535
-
- // The altitude of the viewer. (Above ground)
- #define USERALT 32
-
- // How far to look out over the horizon, the large the value the slower it is
- #define HORIZON_DISTANCE 90
-
- // How fast the viewer moves, the larger the value, the faster but also the
- // more choppier the screen will be.
- #define VIEWER_SPEED 3
-
-
- #define ABS(a) ((a < 0) ? -a : a)
- #define SGN(a) ((a < 0) ? -1 : 1)
-
-
- #define KEYBD 0x9 // Keyboard interrupt
- #define RIGHT_ARROW_KEY 77
- #define UP_ARROW_KEY 72
- #define LEFT_ARROW_KEY 75
- #define DOWN_ARROW_KEY 80
- #define MINUS_KEY 74
- #define PLUS_KEY 78
- #define NUMBER_5_KEY 76
- #define ESCAPE_KEY 1
- #define PGUP_KEY 73
- #define PGDN_KEY 81
- #define B_KEY 48
- #define C_KEY 46
- #define F_KEY 33
- #define I_KEY 23
- #define R_KEY 19
- #define S_KEY 31
- #define W_KEY 17
- #define NUM_1_KEY 2
- #define NUM_2_KEY 3
- #define NUM_3_KEY 4
- #define NUM_4_KEY 5
- #define NUM_5_KEY 6
- #define NUM_6_KEY 7
- #define NUM_7_KEY 8
- #define NUM_8_KEY 9
- #define NUM_9_KEY 10
-
- UCHAR scanCode;
- UCHAR KeyPressed;
- UCHAR MiniKey;
- UCHAR Keys[128];
-
- short TopRow;
- short HorizonTilt;
- short CurrentAlt;
- UCHAR *Video;
- UCHAR *Buffer;
- UCHAR *World;
- UCHAR *Color;
- UCHAR *Sky;
- UCHAR *BackDrop;
- short USERX;
- short USERY;
- short USERA;
- short xPosns[MAX_ANGLE];
- short yPosns[MAX_ANGLE];
- long sine[MAX_ANGLE];
- long cosine[MAX_ANGLE];
- short startpostable[MAXLENGTH+50];
- short startposOrg[MAXLENGTH+50];
- unsigned short OffsetTable[200];
- long RangeTable[HORIZON_DISTANCE * 2];
-
- short RowTable[200];
- short ColorTable[200];
- short UserAlt;
- UCHAR Used[200];
- struct VgaPalette PalBuf[256];
-
- void (__interrupt __far *oldvec)();
- void __interrupt __far myInt();
-
- #if USE_ASSEMBLER
- void CopyVertical(void);
- void FillLinearBuffer(short startpos,short length);
- void FillUsedBuffer(void);
- #endif
-
- extern UCHAR colordat[];
-
-
- //=============================================================================
- // Keyboard interrupt 9
- //=============================================================================
- void __interrupt __far myInt(void)
- {
- register char x;
-
- // oldvec(); // Use when screen captures are wanted - calls orig vector
-
- scanCode = inp(0x60); // read keyboard data port
- x = inp(0x61);
- outp(0x61, (x | 0x80));
- outp(0x61, x);
- outp(0x20, 0x20);
-
- Keys[scanCode & 127] = 1;
- KeyPressed = 1;
- if (scanCode & 128)
- {
- Keys[scanCode & 127] = 0;
- KeyPressed = 0;
- }
- else
- MiniKey = 1;
-
- }
-
- //=============================================================================
- // Sets the 256 color palette. pBuf contains the RGB values to set
- //=============================================================================
- void SetPalette(UCHAR *pbuf)
- {
- short cnt,index;
-
- cnt = 256;
- index = 0;
-
- while (cnt-- > 0)
- {
- outp(0x3c8,index++);
- outp(0x3c9,*pbuf++);
- outp(0x3c9,*pbuf++);
- outp(0x3c9,*pbuf++);
- }
-
- }
-
- //=============================================================================
- //
- //=============================================================================
- void SetVideoMode(short Mode)
- {
- union REGPACK regs;
-
- memset(®s,0,sizeof(union REGPACK)); // Make sure segments are zero
- regs.w.ax = Mode; // Set the mode we want to go to
- intr(0x10,®s); // Use INT 10h to set mode
-
- }
-
- //=============================================================================
- //
- //=============================================================================
- short LoadFiles(void)
- {
- FILE *Data;
- UCHAR *bPtr;
- struct PcxPix PcxBuf;
-
- if (load_pcx("ht.pcx",&PcxBuf,PalBuf) != pcx_ok)
- return(1);
-
- BackDrop = PcxBuf.image;
-
- if (load_pcx("sky.pcx",&PcxBuf,PalBuf) != pcx_ok)
- return(2);
-
- Sky = PcxBuf.image;
-
- if (load_pcx("Altitude.pcx",&PcxBuf,PalBuf) != pcx_ok)
- return(3);
-
- bPtr = PcxBuf.image;
-
- World = malloc(65536); // Get a buffer for entire area
- if (World == NULL)
- return(3);
-
- memmove(World,bPtr,64000);
- memmove(&World[64000],bPtr,1536); // Then duplicate some rows
- free(bPtr);
-
-
- if (load_pcx("Color.pcx",&PcxBuf,PalBuf) != pcx_ok)
- return(4);
-
- bPtr = PcxBuf.image;
-
- Color = malloc(65536); // Get a buffer for the entire area
-
- if (Color == NULL)
- return(4);
-
- memmove(Color,bPtr,64000);
- memmove(&Color[64000],bPtr,1536); // Then duplicate some rows
- free(bPtr);
-
- return(0);
- }
-
- //=============================================================================
- // Calculate a circular area around the center origin that will be the
- // coordinates for our viewing area. These coordinates will then be added to
- // the viewers current X,Y coordinates to give us the endpoints of the line
- // we will cast.
- //=============================================================================
- void SetViewDistance(void)
- {
- short Angle;
-
- for (Angle = 0; Angle < MAX_ANGLE; Angle++)
- {
- xPosns[Angle] = (cosine[Angle] * HORIZON_DISTANCE) >> 14;
- yPosns[Angle] = (sine[Angle] * HORIZON_DISTANCE) >> 14;
- }
-
-
- }
-
- //=============================================================================
- // Setup our two arrays for quicker sine and cosine calculations.
- //=============================================================================
- void InitSinCos(void)
- {
- int a;
- float A;
-
- for (a = 0;a < MAX_ANGLE;a++)
- {
- A = a; // Get angle in a float
- A = (A * M_PI) / (MAX_ANGLE/2); // Convert degrees to radians
- sine[a] = sin(A) * 16384; // Get fixed point sine 2^14
- cosine[a] = cos(A) * 16384; // Get fixed point cosine 2^14
- }
-
- SetViewDistance();
-
- }
-
- //=============================================================================
- //
- //=============================================================================
- void InitStartPosTable(void)
- {
- int i,distance;
- long ht;
-
- ht = (long)16 << 14; // Set fixed point height
-
- for (distance = 1; distance < (HORIZON_DISTANCE*2); distance++)
- {
- RangeTable[distance] = ht / distance; // Setup height range table
- }
-
- for (i = 1;i < (RAYLENGTH+50);i++)
- {
- startposOrg[i] = startpostable[i] = 500 / i; // Used for rows on screen
- startpostable[i] += HorizonTilt;
- }
-
- for (i = 0; i < 200; i++)
- OffsetTable[i] = i * 320; // Faster video offset lookups
-
- }
-
- long OldPos;
- long HighRow;
- long CurrentColumn;
- long CurrentAngle;
- UCHAR CurrentColor;
-
- //=============================================================================
- //
- //=============================================================================
- void DrawLine(void)
- {
- short i,x1,y1,x2,y2;
- short length,startpos;
- long height;
- short d,ax,ay,sx,sy,dx,dy,yOff;
- short BegRow,NumRows;
- unsigned short offset;
- unsigned char c;
- short *sptPtr;
- UCHAR *bPtr;
-
-
- OldPos = 199; // Used to speed up drawing
- HighRow = 200; // Marker of highest row drawn to
-
- offset = OffsetTable[USERY] + USERX; // Current location in color map
- CurrentColor = Color[offset]; // Current color at viewer location
-
- #if USE_ASSEMBLER
- FillUsedBuffer();
- #else
- memset(&Used[100],CurrentColor,100); // Pre-fill buffer to cover holes????
- #endif
-
- x1 = USERX + xPosns[CurrentAngle]; // Get our most distance coordinates
- y1 = USERY + yPosns[CurrentAngle];
- x2 = USERX; // Put viewer coordinates into temps
- y2 = USERY;
-
- offset = y1 * WORLDX + x1; // Calculate offset into buffers
- dx = x2-x1; // Get our delta X
- ax = abs(dx) << 1; // Get our delta X * 2 for difference checks
- sx = SGN(dx); // Get 1 or -1 based on sign of delta X
- dy = y2-y1; // Get our delta Y
- ay = abs(dy) << 1; // Delta Y * 2 for difference checks
- sy = SGN(dy); // Get 1 or -1 based on sign of delta Y
- yOff = WORLDX; // Assume a positive sign and get offset
- if (sy < 0) // If negative direction then
- yOff = -WORLDX; // use a minus offset (this is a row offset)
-
- if (ax > ay) // Is our X difference greater than Y diff?
- {
- d = ay - (ax >> 1); // Yes, calc the error term to use
- length = abs(dx); // Length of the line to plot
- sptPtr = &startpostable[length]; // Use pointer to avoid indexing
- for (i = length; i > 0; i--)
- {
- CurrentColor = Color[offset]; // Color to use for this segment of column
- // Height is calculated by taking the height of the current location
- // subtracting the viewer height, then multiplying by the pre-calc'd
- // distance perspective table divided by the current length of the
- // line. See how RangeTable is setup for further details. The table is
- // used so a faster multiply can be done vs a slower divide by length
- // for each unit of the line.
- height = ((World[offset] - UserAlt) * RangeTable[i]) >> 14;
-
- // Perspective transform takes a predetermined height for the length
- // of the line and subtracts the calculated height above. This gives
- // us an actual height for the location we are currently examining.
- // Note below that startpos is actual a delta height greater than
- // the last height we calculated. OldPos is used to remember the last
- // height we found.
- startpos = *sptPtr - height;
- sptPtr--;
-
- #if USE_ASSEMBLER
- FillLinearBuffer(startpos,i);
- #else
- if (startpos > OldPos) // Are we beyond the last height?
- {
- // Are we in bounds for the screen vertical height?
- if (startpos > 0 && startpos < 200 && OldPos < 200)
- {
- if (i < 10) // Check our length and fill from
- { // the bottom up
- NumRows = 200 - OldPos; // for the last height remembered.
- BegRow = 199;
- }
- else
- {
- // Otherwise start with current minus last as number of rows
- NumRows = startpos - OldPos;
- BegRow = startpos;
- }
-
- BegRow -= NumRows; // We want to build downward not up.
- if (BegRow < 0) // If starting row went negative
- {
- NumRows += BegRow; // Adjust the number of rows
- BegRow = 0; // And start at top of column
- }
-
- if (BegRow < HighRow) // Keep our high water mark updated
- HighRow = BegRow;
-
-
- // Copy the color into a buffer which will later be used
- // to display the vertical column.
- memset(&Used[BegRow],CurrentColor,NumRows);
- }
- }
- #endif
-
- OldPos = startpos; // Now update our remembered height
-
- if( d >= 0 ) // Check error term to see if Y coordinate
- { // needs to be adjusted
- offset += yOff; // Bump buffer offset to next row
- d -= ax; // and reset our error term
- }
-
- offset += sx; // Bump buffer offset to next column
- d += ay; // and adjust our error term
- }
- }
- else // Here if the Y difference is >= the X difference
- {
- d = ax - (ay >> 1);
- length = abs(dy);
- sptPtr = &startpostable[length]; // Use pointer to avoid indexing
- for (i = length; i > 0; i--)
- {
- CurrentColor = Color[offset];
- height = ((World[offset] - UserAlt) * RangeTable[i]) >> 14;
-
- // Perspective transform
- startpos = *sptPtr - height; // Get perspective height for this distance
- sptPtr--;
-
- #if USE_ASSEMBLER
- FillLinearBuffer(startpos,i);
- #else
- if (startpos > OldPos)
- {
- if (startpos > 0 && startpos < 200 && OldPos < 200)
- {
- if (i < 10)
- {
- NumRows = 200 - OldPos;
- BegRow = 199;
- }
- else
- {
- NumRows = startpos - OldPos;
- BegRow = startpos;
- }
-
- BegRow -= NumRows;
- if (BegRow < 0)
- {
- NumRows += BegRow;
- BegRow = 0;
- }
- if (BegRow < HighRow)
- HighRow = BegRow;
- memset(&Used[BegRow],CurrentColor,NumRows);
- }
- }
- #endif
-
-
- OldPos = startpos; // Used to speed up rendering.
-
- if( d >= 0 )
- {
- offset += sx;
- d -= ay;
- }
- offset += yOff;
- d += ax;
- }
- }
-
- // Now copy our buffer to the vertical column of the screen
-
- #if USE_ASSEMBLER
- CopyVertical();
- #else
- bPtr = Buffer + OffsetTable[HighRow] + CurrentColumn;
- for (i = HighRow; i < 200; i++)
- {
- c = Used[i];
- *bPtr = c;
- bPtr[1] = c;
- bPtr += 320;
- }
- #endif
-
- }
-
-
- //=============================================================================
- //
- //=============================================================================
- void ShowScreen(void)
- {
-
- memmove(Video,Buffer,51200);
-
- }
-
- //=============================================================================
- //
- //=============================================================================
- void ShowBackDrop(void)
- {
-
- memmove(Video,BackDrop,64000);
-
- }
-
-
-
- //=============================================================================
- //
- //=============================================================================
- void DrawSkyRange(short Angle,short column,short wt,short rows)
- {
- UCHAR *SkyPtr,*BufPtr;
-
- SkyPtr = Sky + Angle; // Get offset into Sky buffer
- BufPtr = Buffer + column; // Video buffer column to start with
- while (rows-- > 0)
- {
- memmove(BufPtr,SkyPtr,wt); // Move width from Sky to buffer
- BufPtr += 320; // Next row of Video buffer
- SkyPtr += 320; // Next row of Sky buffer
- }
-
- }
-
- //=============================================================================
- //
- //=============================================================================
- void DrawView(void)
- {
- short column,Angle,px,py;
- short Alt1,Alt2;
- unsigned short offset;
-
-
- // Draw the sky in 2 sections, first start out with the column of the sky
- // for our current angle and draw as much as possible, then start with column
- // zero of the sky and draw any remaining columns
-
- Angle = USERA % 320; // Get starting column of sky buffer
- px = 320 - Angle; // Get width of sky we can draw
- if (px)
- DrawSkyRange(Angle,0,px,150); // Draw 150 rows of width
-
- py = 320 - px; // Now get remaining width we need
- if (py)
- DrawSkyRange(0,px,py,150); // And draw from column 0 of sky
-
- Angle = USERA + 160; // Angle plus half the screen
- if (Angle >= MAX_ANGLE) // Check if beyond max angle range
- Angle -= MAX_ANGLE; // and bring back down into range
-
- px = USERX + ((cosine[Angle] * 6) >> 14);
- py = USERY + ((sine[Angle] * 6) >> 14);
- offset = (py * WORLDX) + px;
- Alt1 = World[offset]; // Get the height alittle in front
-
-
- offset = (USERY * WORLDX) + USERX; // Get the offset into our world
- px = World[offset]; // Get the altitude of the viewer
-
- if (px > CurrentAlt) // Are we higher than our viewer height?
- UserAlt = px + CurrentAlt; // Yes, set to new height
- else
- UserAlt = CurrentAlt; // Otherwise stay at viewer height
-
- if (Alt1 > UserAlt) // Is height in front of us higher?
- UserAlt = Alt1 + CurrentAlt; // Yes, set to new height
-
- CurrentAngle = USERA; // Start with left side of screen
-
- for (CurrentColumn = 0; CurrentColumn < 320; CurrentColumn += COLUMN_AMOUNT)
- {
- DrawLine(); // Draw current vertical column
- CurrentAngle += COLUMN_AMOUNT; // Next Angle on the screen
- if (CurrentAngle >= MAX_ANGLE) // Check if we wrapped around
- CurrentAngle -= MAX_ANGLE;
- }
-
- ShowScreen(); // Copy buffer to screen
-
- }
-
- //=============================================================================
- //
- //=============================================================================
- void DrawScreen(void)
- {
- short done,Angle,i;
- short SpinAngle;
-
- memset(Buffer,0,64000);
- done = 0;
- SpinAngle = MAX_ANGLE >> 5;
-
- while (!done)
- {
- DrawView();
-
- if (Keys[ESCAPE_KEY])
- break;
-
- if (Keys[UP_ARROW_KEY])
- {
- Angle = USERA + HALF_SCREEN;
- if (Angle >= MAX_ANGLE) Angle -= MAX_ANGLE;
-
- USERX = USERX + ((cosine[Angle] * VIEWER_SPEED) >> 14);
- USERY = USERY + ((sine[Angle] * VIEWER_SPEED) >> 14);
- USERX = USERX % WORLDX;
- USERY = USERY % WORLDY;
- }
-
- if (Keys[DOWN_ARROW_KEY])
- {
- Angle = USERA + HALF_SCREEN + (MAX_ANGLE/2);
- if (Angle >= MAX_ANGLE) Angle -= MAX_ANGLE;
-
- USERX = USERX + ((cosine[Angle] * VIEWER_SPEED) >> 14);
- USERY = USERY + ((sine[Angle] * VIEWER_SPEED) >> 14);
- USERX %= WORLDX;
- USERY %= WORLDY;
- }
-
- if (Keys[LEFT_ARROW_KEY])
- {
- USERA = USERA - SpinAngle;
- if (USERA < 0) USERA += MAX_ANGLE;
- }
-
- if (Keys[RIGHT_ARROW_KEY])
- {
- USERA = USERA + SpinAngle;
- if (USERA >= MAX_ANGLE) USERA -= MAX_ANGLE;
- }
-
- if (Keys[PGUP_KEY])
- {
- if (HorizonTilt > 4)
- {
- HorizonTilt--;
- for (i = 0; i < (RAYLENGTH+50); i++)
- startpostable[i] = startposOrg[i] + HorizonTilt;
-
- }
- }
-
- if (Keys[PGDN_KEY])
- {
- if (HorizonTilt < 90)
- {
- HorizonTilt++;
- for (i = 0; i < (RAYLENGTH+50); i++)
- startpostable[i] = startposOrg[i] + HorizonTilt;
- }
- }
-
- if (Keys[MINUS_KEY])
- {
- if (CurrentAlt > 8)
- CurrentAlt--;
-
- }
-
- if (Keys[PLUS_KEY])
- {
- if (CurrentAlt < 100)
- CurrentAlt++;
- }
-
- }
-
- }
-
- //=============================================================================
- //
- //=============================================================================
- void main(short argc,char **argv)
- {
- short result;
-
- HorizonTilt = 80; // Initialize our Horizon
- CurrentAlt = USERALT; // And default altitude
- InitSinCos(); // Build our trig tables
- InitStartPosTable(); // and Height to Row tables
- result = LoadFiles(); // Load the pictures
- if (result)
- {
- printf("Error: %d while loading pictures.\n",result);
- return;
- }
-
- Buffer = malloc(64000); // Init the buffer we'll draw to
- if (Buffer == NULL)
- {
- printf("Not enough memory.\n");
- return;
- }
-
- USERX=160; // Starting X,Y coordinates
- USERY=100;
- USERA=960; // Starting angle.
-
- Video = (UCHAR *)0xA0000; // Address of VGA screen
- oldvec=_dos_getvect(KEYBD); // Get the current keyboard vector
- _dos_setvect(KEYBD,myInt); // And replace it with ours
-
- SetVideoMode(0x13); // Switch to 320x200 256 color mode
- SetPalette((UCHAR *)PalBuf); // And set our palette
- ShowBackDrop(); // Show the main screen image
- DrawScreen(); // Show terrain and allow moving
-
- SetVideoMode(0x03); // Back to text color 80 mode
-
- _dos_setvect(KEYBD,oldvec); // Put back the old keyboard vector
-
- }
-
-